package com.ruoyi.common.flowable;

import cn.hutool.extra.spring.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.impl.interceptor.Command;
import org.flowable.common.engine.impl.interceptor.CommandContext;
import org.flowable.engine.impl.RuntimeServiceImpl;
import org.flowable.engine.impl.cmd.AbstractDynamicInjectionCmd;
import org.flowable.engine.impl.dynamic.BaseDynamicSubProcessInjectUtil;
import org.flowable.engine.impl.dynamic.DynamicUserTaskBuilder;
import org.flowable.engine.impl.persistence.entity.DeploymentEntity;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.flowable.engine.impl.util.CommandContextUtil;
import java.util.*;


/**
 * @author 艾付强
 * 动态增加任务节点
 */
@Slf4j
public class AfterMultipleUserTaskCmd extends AbstractDynamicInjectionCmd implements Command<Void> {

    //后加签的节点信息
    private DynamicUserTaskBuilder signUserTaskBuilder;

    //流程实例id
    protected String processInstanceId;

    //当前流程节点的id
    private String prevActivityId;

    private Process process;

    private BpmnModel bpmnModel;

    private CommandContext commandContext;

    private boolean sequential;
    private String executionId;



    public AfterMultipleUserTaskCmd(String prevActivityId, String processInstanceId, DynamicUserTaskBuilder signUserTaskBuilder) {
        this.processInstanceId = processInstanceId;
        this.prevActivityId = prevActivityId;
        this.signUserTaskBuilder = signUserTaskBuilder;
    }

    public AfterMultipleUserTaskCmd setSequential(boolean sequential) {
        this.sequential = sequential;
        return this;
    }

    public AfterMultipleUserTaskCmd setExecutionId(String executionId) {
        this.executionId = executionId;
        return this;
    }

    @Override
    public Void execute(CommandContext commandContext) {
        //AbstractDynamicInjectionCmd提供的修改方法入口
        createDerivedProcessDefinitionForProcessInstance(commandContext, processInstanceId);
        return null;
    }

    @Override
    protected void updateBpmnProcess(CommandContext commandContext, Process process,
                                     BpmnModel bpmnModel, ProcessDefinitionEntity originalProcessDefinitionEntity, DeploymentEntity newDeploymentEntity) {
        this.process = process;
        this.bpmnModel = bpmnModel;
        this.commandContext = commandContext;


        FlowElement previousFlowElement = process.getFlowElement(prevActivityId, true);

        Activity previousActivity = (Activity) previousFlowElement;
        addUserTask(previousActivity);

        clearBpmnModel();

        //当前流程的重新定义
        BaseDynamicSubProcessInjectUtil.processFlowElements(commandContext, process, bpmnModel, originalProcessDefinitionEntity, newDeploymentEntity);
    }

    private Activity addUserTask(Activity previousActivity) {
        //由于是后加签所以需要获取当前流程节点流出的sequence
        SequenceFlow currentTaskOutSequenceFlow = previousActivity.getOutgoingFlows().get(0);

        List<String> candidateUser = Arrays.asList(signUserTaskBuilder.getAssignee().split(","));
        //定义新的节点
        UserTask addUserTask = createUserTaskCounterSign(signUserTaskBuilder.getId(), signUserTaskBuilder.getName(), candidateUser);

        process.addFlowElement(addUserTask);

        //定义新的sequence 将节点的source改为新的节点  target 改为当前节点的target
        SequenceFlow newSequenceFlow = this.createSequenceFlow(addUserTask.getId(), currentTaskOutSequenceFlow.getTargetFlowElement().getId());
        process.addFlowElement(newSequenceFlow);

        previousActivity.setOutgoingFlows(Collections.singletonList(newSequenceFlow));
        //修改原有sequence 将原来的sequence的target改为新的节点
        currentTaskOutSequenceFlow.setTargetFlowElement(addUserTask);
        currentTaskOutSequenceFlow.setTargetRef(addUserTask.getId());
        return addUserTask;
    }

    //会签
    protected UserTask createUserTaskCounterSign(String id, String name, List<String> candidateUser) {
        String inputDataItem =  id + "AssigneeList";
        UserTask userTask = new UserTask();
        // 流程名称
        userTask.setName(name);
        userTask.setId(id);
        userTask.setAssignee("${assignee}");

        Map<String,Object> vars = new HashMap<>();
        vars.put(inputDataItem,candidateUser);

        SpringUtil.getBean(RuntimeServiceImpl.class).setVariables(this.executionId, vars);
        MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = new MultiInstanceLoopCharacteristics();
        // 设置并行执行(每个审批人可以同时执行）
        multiInstanceLoopCharacteristics.setSequential(this.sequential);
        // 设置完成条件 nrOfInstances--实例总数、 nrOfActiveInstances--还没有完成的数量   nrOfCompletedInstances--完成审批的数量
        multiInstanceLoopCharacteristics.setCompletionCondition("${nrOfCompletedInstances/nrOfInstances >= 0.99}");
        //multiInstanceLoopCharacteristics.setLoopCardinality(loopCardinality);
        //审批人集合参数
        multiInstanceLoopCharacteristics.setInputDataItem(inputDataItem);
        //迭代集合
        multiInstanceLoopCharacteristics.setElementVariable("assignee");
        //设置多实例属性
        userTask.setLoopCharacteristics(multiInstanceLoopCharacteristics);
        //设置审批人
        //userTask.setCandidateUsers(candidateUser);
        return userTask;
    }


    //clean清除原有的一些图的坐标信息
    private void clearBpmnModel() {
        bpmnModel.getLocationMap().clear();
        bpmnModel.getFlowLocationMap().clear();
        bpmnModel.getLabelLocationMap().clear();
    }

    @Override
    protected void updateExecutions(CommandContext commandContext, ProcessDefinitionEntity processDefinitionEntity,
                                    ExecutionEntity executionEntity, List<ExecutionEntity> list) {

    }

    //这个方法也可以单独写在一个工具类里
    public SequenceFlow createSequenceFlow(String from, String to) {
        SequenceFlow flow = new SequenceFlow();
        flow.setSourceRef(from);
        flow.setTargetRef(to);
        flow.setId("sequenceFlow-" + CommandContextUtil.getProcessEngineConfiguration(commandContext).getIdGenerator().getNextId());
        return flow;
    }
}